const fs = require('fs');
const path = require('path');
const request = require('request');
const AWS = require('aws-sdk');
const { dialog } = require('electron').remote;
const EventEmitter = require('events').EventEmitter;

const downloadDir = path.join(process.env['HOME'], 'Downloads');

class S3Manager {
    constructor(config) {
        this.config = Object.assign({}, config);
        AWS.config.update({
            region: config.region,
            accessKeyId: config.accessKey,
            secretAccessKey: config.secretKey
        });
        this.s3Client = new AWS.S3();
    }

    listBuckets = callback => {
        console.log(this.config);

        this.s3Client.listBuckets((err, content) => {
            if (err) {
                console.error(err);
            }
            const bucketNames = content.Buckets.map(b => b.Name);
            console.log(bucketNames);
            callback(bucketNames);
        });
    };

    listOneLayerKeys = (bucket, callback) => {
        var params = {
            Bucket: bucket,
            Delimiter: '/'
        };
        this.s3Client.listObjectsV2(params, function(err, data) {
            if (err) console.log(err, err.stack);
            // an error occurred
            else {
                const folders = data.CommonPrefixes.map(c => c.Prefix).reverse();
                const files = data.Contents.map(c => c.Key);
                const keys = [...folders, ...files];
                callback(keys);
            }
            console.log(data); // successful response
        });
    };

    listNextLayerKeys = (bucket, prefix, callback) => {
        var params = {
            Bucket: bucket,
            Delimiter: '/',
            Prefix: prefix
        };
        console.log('listNextLayerKeys');
        console.log(params);

        this.s3Client.listObjectsV2(params, function(err, data) {
            if (err) console.log(err, err.stack);
            // an error occurred
            else {
                const folders = data.CommonPrefixes.map(c => c.Prefix).reverse();
                const files = data.Contents.map(c => c.Key);
                let keys = [...folders, ...files];
                keys = keys
                    .map(key => key.slice(prefix.length))
                    .filter(key => key.length > 0);
                callback(keys);
            }
            console.log(data); // successful response
        });
    };

    downloadFromS3 = (bucket, key, startHandler = null, progressHandler = null) => {
        const url = this.s3Client.getSignedUrl('getObject', {
            Bucket: bucket,
            Key: key,
            Expires: 60 * 5
        });
        console.log(url);
        console.log(key);

        const fileName = key
            .slice(key.lastIndexOf('/') + 1)
            .replace(/[^a-z0-9_.@()-]/g, '_');
        console.log(fileName);
        const defaultFileName = path.join(downloadDir, fileName);

        dialog.showSaveDialog({ defaultPath: defaultFileName }, fileName => {
            this.downloadFile(url, fileName, startHandler, progressHandler);
        });
        return url;
    };

    downloadFile = (url, targetPath, startHandler = null, progressHandler = null) => {
        if (!targetPath) {
            return;
        }
        let received_bytes = 0;
        let total_bytes = 0;

        let req = request({
            method: 'GET',
            uri: url
        });

        let out = fs.createWriteStream(targetPath);
        req.pipe(out);

        req.on('response', function(data) {
            // Change the total bytes value to get progress later.
            total_bytes = parseInt(data.headers['content-length']);
            startHandler && startHandler();
        });

        req.on('data', function(chunk) {
            // Update the received bytes
            received_bytes += chunk.length;

            progressHandler && progressHandler(received_bytes / total_bytes * 100.0);
            // showProgress(received_bytes, total_bytes);
        });

        req.on('end', function() {
            alert('File succesfully downloaded');
            progressHandler && progressHandler(100);
        });
    };

    deleteS3Objects = (bucket, keys, callback) => {
        const toDeleteObjects = keys.map(key => {
            return {
                Key: key
            };
        });

        var params = {
            Bucket: bucket,
            Delete: {
                Objects: [...toDeleteObjects],
                Quiet: false
            }
        };

        console.log(params);

        this.s3Client.deleteObjects(params, () => {
            callback();
        });
    };

    deleteS3Object = (bucket, key) => {
        this.s3Client.deleteObject(
            {
                Bucket: bucket,
                Key: key
            },
            () => {
                console.log('delete done');
            }
        );
    };

    describeS3Object = (bucket, key, fn) => {
        this.s3Client.headObject({
            Bucket: bucket,
            Key: key
        }, (err, data) => {
            if (err) console.log(err, err.stack);
            else {
                fn(data);
            }
        });
    };

    uploadS3Objects = (bucket, prefix, fn) => {
        dialog.showOpenDialog(
            {
                properties: ['openFile', 'multiSelections']
            },
            fileNames => {
                if (!fileNames) {
                    return;
                }
                console.log(fileNames);

                let counter = fileNames.length;
                const emitter = new EventEmitter();
                emitter.on('done', () => {
                    counter = counter - 1;
                    if (counter === 0) {
                        fn();
                    }
                });

                for (const fileName of fileNames) {
                    fs.readFile(fileName, (err, content) => {
                        const params = {
                            Body: content,
                            Bucket: bucket,
                            Key: prefix + path.basename(fileName)
                        };

                        this.s3Client.putObject(params, function(err, data) {
                            if (err) console.log(err, err.stack);
                            else console.log(data);

                            emitter.emit('done');
                        });
                    });
                }
            }
        );
    };
}

let instance;

export const getS3Manager = config => {
    if (!instance) {
        if (config) {
            instance = new S3Manager(config);
        } else {
            console.error('No S3Manager created and config param is null');
        }
    }
    return instance;
};

export const unpateS3Manager = config => {
    instance = new S3Manager(config);
    return instance;
};
